// Copyright 2010, Vanya Davidenko.
// Используемая кодировка: UTF-8.

#ifndef LIBV_CISTRING_H_
#define LIBV_CISTRING_H_

#ifdef _WIN32
# pragma warning(push)
# pragma warning(disable:4996)  // Function may be unsafe...
#endif


#include <cstring>
#include <cctype>
#include <string>
#include <iostream>
#include <iosfwd>


namespace libv
{

/** Traits для символьных типов, игнорирующие регистр букв.
  * Корректно работают только для ASCII-строк, так как в юникоде перевод
  * регистра букв иногда зависит от контекста (например, в греческом).
  * Большинство функций наследуется из стандартного класса.
  * Реализованы только те методы, которые необходимо затенить (override).
  * @sa http://www.sgi.com/tech/stl/char_traits.html */
struct CICharTraits : public ::std::char_traits<char>
{
    static bool eq(const char_type& lhv, const char_type& rhv)
    {
        return ::std::toupper(lhv) == ::std::toupper(rhv);
    }


    static bool ne(const char_type& lhv, const char_type& rhv)
    {
        return !eq(lhv, rhv);
    }


    static bool lt(const char_type& lhv, const char_type& rhv)
    {
        return ::std::toupper(lhv) < ::std::toupper(rhv);
    }


    static int compare(const char_type* lhv, const char_type* rhv, size_t n)
    {
#ifdef _WIN32
        return _memicmp(lhv, rhv, n);
#else
        return strncasecmp(lhv, rhv, n);
#endif
    }


    static const char* find(const char_type* s, size_t n, const char_type& a)
    {
        for ( size_t i = 0 ; i != n ; ++i )
        {
            if ( eq(s[i], a) )
                return s + i;
        }
        return NULL;
    }


    static bool eq_int_type(const ::std::char_traits<char>::int_type& c1,
                            const ::std::char_traits<char>::int_type& c2)
    {
        return ::std::toupper(c1) == ::std::toupper(c2) ;
    }


    // Visual C++ выдает предупреждения о небезопасных функциях в стандартных
    // заголовочных файлах. Добавив эти 2 метода их число уменьшается с 4 до 2.
    // Но все равно нужно явно использовать #define _SCL_SECURE_NO_WARNINGS.
#ifdef _MSC_VER
    static char_type* move(char_type* dst, const char_type* src, size_t n)
    {
        return static_cast<char_type*>(
                   ::std::memmove(dst, src, n * sizeof(char_type)));
    }


    static char_type* copy(char_type* dst, const char_type* src, size_t n)
    {
        return static_cast<char_type*>(
                   ::std::memcpy(dst, src, n * sizeof(char_type)));
    }
#endif
};



/** Класс регистронезависимых строк, полностью совместимый с ::std::string
  * интерфейсами, а также может создаваться и редактироваться через. */
class CIString : public ::std::basic_string<char, CICharTraits>
{
  private:
    typedef ::std::basic_string<char, CICharTraits> BasicImpl;

  public:
    CIString()
        : BasicImpl() {}

    CIString(const CIString& rhv)
        : BasicImpl(rhv) {}

    CIString(const CIString& rhv, size_t pos, size_t n = npos)
        : BasicImpl(rhv, pos, n) {}

    CIString(const char* s, size_t n)
        : BasicImpl(s, n) {}

    CIString(const char* s)
        : BasicImpl(s) {}

    CIString(const size_t n, const char c)
        : BasicImpl(n, c) {}

    template<class InputIterator>
    CIString(InputIterator begin, InputIterator end)
        : BasicImpl(begin, end) {}

    CIString(const ::std::string& rhv)
        : BasicImpl(rhv.data(), rhv.size()) {}


    using BasicImpl::operator=;

    CIString& operator=(const ::std::string& rhv)
    {
        BasicImpl::assign(rhv.data(), rhv.size());
        return *this;
    }


    using BasicImpl::begin;
    using BasicImpl::end;
    using BasicImpl::rbegin;
    using BasicImpl::rend;


    using BasicImpl::length;
    using BasicImpl::max_size;
    using BasicImpl::resize;
    using BasicImpl::capacity;
    using BasicImpl::reserve;
    using BasicImpl::clear;
    using BasicImpl::empty;


    using BasicImpl::operator[];
    using BasicImpl::at;


    using BasicImpl::operator+=;

    CIString& operator+=(const ::std::string& rhv)
    {
        BasicImpl::append(rhv.data(), rhv.size());
        return *this;
    }

    using BasicImpl::append;

    CIString& append(const ::std::string& rhv)
    {
        BasicImpl::append(rhv.data(), rhv.size());
        return *this;
    }

    CIString& append(const ::std::string& rhv, size_t pos, size_t n)
    {
        // n может быть больше хвоста строки, тогда берется меньшее число.
        // поэтому rhv должна быть нуль-терменирована.
        BasicImpl::append(rhv.c_str() + pos, n);
        return *this;
    }

    using BasicImpl::push_back;

    using BasicImpl::assign;

    CIString& assign(const ::std::string& rhv)
    {
        BasicImpl::assign(rhv.data(), rhv.size());
        return *this;
    }

    CIString& assign(const ::std::string& rhv, size_t pos, size_t n)
    {
        BasicImpl::assign(rhv.c_str() + pos, n);
        return *this;
    }

    using BasicImpl::insert;

    CIString& insert(const size_t pos, const ::std::string& rhv)
    {
        BasicImpl::insert(pos, rhv.data(), rhv.size());
        return *this;
    }

    CIString& insert(size_t pos1,
                     const ::std::string& rhv, size_t pos2, size_t n)
    {
        BasicImpl::insert(pos1, rhv.c_str() + pos2, n);
        return *this;
    }

    using BasicImpl::erase;

    using BasicImpl::replace;

    CIString& replace(size_t pos, size_t n, const ::std::string& str)
    {
        BasicImpl::replace(pos, n, str.data(), str.size());
        return *this;
    }

    CIString& replace(iterator i1, iterator i2, const ::std::string& str)
    {
        BasicImpl::replace(i1, i2, str.data(), str.size());
        return *this;
    }

    CIString& replace(size_t pos, size_t n,
                      const ::std::string& str, size_t pos2, size_t n2)
    {
        BasicImpl::replace(pos, n, str.c_str() + pos2, n2);
        return *this;
    }

    using BasicImpl::swap;


    using BasicImpl::c_str;
    using BasicImpl::data;
    using BasicImpl::get_allocator;
    using BasicImpl::copy;

    using BasicImpl::find;

    size_t find(const ::std::string& str, size_t pos = 0) const
    {
        return BasicImpl::find(str.c_str(), pos);
    }

    using BasicImpl::rfind;

    size_t rfind(const ::std::string& str, size_t pos = 0) const
    {
        return BasicImpl::rfind(str.c_str(), pos);
    }

    using BasicImpl::find_first_of;

    size_t find_first_of(const ::std::string& str, size_t pos = 0) const
    {
        return BasicImpl::find_first_of(str.c_str(), pos);
    }

    using BasicImpl::find_last_of;

    size_t find_last_of(const ::std::string& str, size_t pos = 0) const
    {
        return BasicImpl::find_last_of(str.c_str(), pos);
    }

    using BasicImpl::find_first_not_of;

    size_t find_first_not_of(const ::std::string& str, size_t pos = 0) const
    {
        return BasicImpl::find_first_not_of(str.c_str(), pos);
    }

    using BasicImpl::find_last_not_of;

    size_t find_last_not_of(const ::std::string& str, size_t pos = 0) const
    {
        return BasicImpl::find_last_not_of(str.c_str(), pos);
    }

    using BasicImpl::substr;

    using BasicImpl::compare;
};





inline ::std::ostream& operator<<(::std::ostream& os, const CIString& str)
{
    // Грубо, зато очень быстро.
    // Посимвольно перегонять в ::std::string — медленно.
    return os << reinterpret_cast<const ::std::string&>(str) ;
}


inline ::std::istream& operator>>(::std::istream& is, CIString& str)
{
    // Для надежности, накапливаем сначала ввод в промежуточную строку.
    std::string tmp;
    is >> tmp;
    if ( !is.fail() )
        str.assign(tmp.begin(), tmp.end());

    return is;
}


inline std::istream& getline(std::istream& is, CIString& str)
{
    std::string tmp;
    std::getline(is, tmp);
    if ( !is.fail() )
        str.assign(tmp.begin(), tmp.end());

    return is;
}

}  // libv


#ifdef _WIN32
# pragma warning(pop)
#endif

#endif  // LIBV_CISTRING_H_
